/*******************************************************************/
/* @ All Rights Reserved Copyright (C) 2014 broada,Co.,Ltd.        */
/*                                                                 */
/*  NAME      = frct_hash.h                                        */
/*  NOTE      = conntrack管理hash                                  */
/*              本文件中函数线程不安全                             */
/*  DATE      = 2014/10/20 by yaohl                                */
/*******************************************************************/

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

#include "frcomm.h"
#include "frlog.h"
#include "frmem.h"
#include "frhash2.h"
#include "conntrack.h"
#include "frct_comm.h"
#include "frct_mng.h"

#define APM_CT_HASH_SIZE  65536

FR_STATIC Frhash_header        *g_p_st_hash_ct=NULL;
FR_STATIC pthread_spinlock_t   g_spin_ct_lock;
FR_STATIC unsigned long        ul_chk_ct_cnts=0L;

typedef struct __st_ct_compare
{
    u_short ip_proto;             /* 6(IPPROTO_TCP)  / 17(IPPROTO_UDP) */
    u_int src_ip;
    u_short src_port;
    u_int dst_ip;
    u_short dst_port;
} Frct_compare;

/*******************************************************************************
*  FUNC     :  节点内存释放
*  ARGS     :  p_st_node  (Frlist_node *)
*  RTN      :
*  NOTE     :  注意需要在ct->lock下操作
*******************************************************************************/
FR_STATIC void __hash_node_free(Frlist_node *p_st_node)
{
    return;
}

/*******************************************************************************
*  FUNC     :  节点内容出力
*  ARGS     :  p_st_node       (Frlist_node *)
            :  p_outbuff       (出力内容指针)
            :  ui_buff_size    (出力内容空间)
*  RTN      : 
*  NOTE     :
*******************************************************************************/
FR_STATIC void __hash_node_show(Frlist_node *p_st_node, char *p_outbuff, unsigned int ui_buff_size)
{
    Conntrack *p_st_ct = NULL;
    L7_module *p_st_l7_module=NULL;
    char *p_buff = NULL,arr_ip_src[56], arr_ip_dst[56];
    int i_buff_size = 128;

    if(p_st_node == NULL || p_st_node->data == NULL || ui_buff_size <= 0 )
        return;

    ul_chk_ct_cnts++;
    p_st_ct = (Conntrack *)(p_st_node->data);
    p_st_l7_module = p_st_ct->st_l7_analyzer.p_st_l7_module;
    p_buff =(char *) frmalloc(i_buff_size);
    if(p_buff)
    {
        snprintf(p_buff, i_buff_size, "%d(%s%s)"
            , p_st_l7_module->i_l7_proto, p_st_l7_module->p_module_name, p_st_l7_module->p_version);
    }
    snprintf(arr_ip_src, sizeof(arr_ip_src), "%s", int_ntoa(p_st_ct->src_ip));
    snprintf(arr_ip_dst, sizeof(arr_ip_dst), "%s", int_ntoa(p_st_ct->dst_ip));
    //snprintf(p_outbuff, ui_buff_size, "%d(%s)%s:%i->%s:%i refcnt=%u ct_direct=%s master=%s self=%p [l7_analyzer:%s]\n"
    snprintf(p_outbuff, ui_buff_size, "%d(%s)%s:%i->%s:%i refcnt=%u ct_direct=%s self=%p\n:%s\n"
           , p_st_ct->ip_proto, FR_L4PROTO2STR(p_st_ct->ip_proto)
           , arr_ip_src, p_st_ct->src_port, arr_ip_dst, p_st_ct->dst_port
           , p_st_ct->refcnt, p_st_ct->flg_direct_known==FR_DIRECT_KNOWN?"known":"unknown"
           //, arr_master
           , p_st_ct
           , p_buff);
    
    if(p_buff)
        frfree(p_buff);
    return;
}


/*******************************************************************************
*  FUNC     :  初始化ct管理用hash
*  ARGS     :
*  RTN      :  FR_SUCC:成功,FR_FAIL:失败
*  NOTE     :
*******************************************************************************/
int  frct_hash_init(unsigned int ui_table_size)
{
    unsigned int ui_hash_conntrack_size =0;
    int i_ret = FR_FAIL;
    
    if(ui_table_size < 65535)
        ui_hash_conntrack_size = APM_CT_HASH_SIZE; 
    else
        ui_hash_conntrack_size = ui_table_size; 
    
    g_p_st_hash_ct = frhash2_create2(ui_hash_conntrack_size);
    if(g_p_st_hash_ct == NULL)
        return i_ret;
    
    pthread_spin_init(&g_spin_ct_lock, 0);
    
    i_ret = FR_SUCC;
    return i_ret;
}

void frct_hash_wlock(void)
{
    pthread_spin_lock(&g_spin_ct_lock);
    return;
}

void frct_hash_unwlock(void)
{
    pthread_spin_unlock(&g_spin_ct_lock);
    return;
}

void frct_hash_rlock(void)
{
    pthread_spin_lock(&g_spin_ct_lock);
    return;
}

void frct_hash_unrlock(void)
{
    pthread_spin_unlock(&g_spin_ct_lock);
    return;
}


/*******************************************************************************
*  FUNC     :  组成哈希用key
*  ARGS     :  us_proto  (协议)
            :  ui_src    (原地址)
            :  us_sport  (原端口)
            :  ui_dest   (目的地址)
            :  us_dport  (目的端口)
            :  p_key     (保存地址)
            :  ui_key_size(保存地址长度)
*  RTN      :  key长度
*  NOTE     :
*******************************************************************************/
int frct_hash_key(u_short us_proto, u_int ui_src, u_short us_sport, u_int ui_dest, u_short us_dport
                , char *p_key, unsigned int ui_key_size)
{
    int i_idx=0, i_len;
    u_int *stupid_strict_aliasing_warnings=(u_int*)p_key;

    if(p_key == NULL || ui_key_size <= sizeof(u_int)*2+ sizeof(u_short)*3)
    {
        FRLOG_ERROR_DETAIL("ct hash key err");
        return 0;
    }

    i_len = sizeof(u_int);
    *stupid_strict_aliasing_warnings = ui_src;
    i_idx += i_len;

    *(u_int *) (p_key + i_idx) = ui_dest;
    i_idx += i_len;

    i_len = sizeof(u_short);
    *(u_short *) (p_key + i_idx) = us_sport;
    i_idx += i_len;

    *(u_short *) (p_key + i_idx) = us_dport;
    i_idx += i_len;

    *(u_short *) (p_key + i_idx) = us_proto;
    i_idx += i_len;

    return i_idx;
}

/*******************************************************************************
*  FUNC     :  增加ct到hash
*  ARGS     :  p_st_ct  (Conntrack *)
            :  ul_key   (hash key)
*  RTN      :  成功:返回当前ct的节点, 失败:NULL
*  NOTE     :
*******************************************************************************/
Frlist_node *frct_hash_add(Conntrack *p_st_ct, char *p_key, int i_key_len)
{
    Frlist_node *p_st_node=NULL;
    int i_ret_fun;

    if(g_p_st_hash_ct==NULL || p_st_ct==NULL || p_key==NULL || i_key_len <=0)
        return p_st_node;

    p_st_node = frlist_node_create(p_st_ct);
    if(!p_st_node)
        return p_st_node;

    i_ret_fun = frhash2_addStr(g_p_st_hash_ct, p_key, i_key_len, p_st_node);
    if(i_ret_fun == FR_FAIL)
    {
        frfree(p_st_node);
        return NULL;
    }
    return p_st_node;
}

/*******************************************************************************
*  FUNC     :  获取连接的node
*  ARGS     :  us_proto  (协议)
            :  ui_src    (原地址)
            :  us_sport  (原端口)
            :  ui_dest   (目的地址)
            :  us_dport  (目的端口)
*  RTN      :  节点指针:成功， NULL:失败
*  NOTE     :
*******************************************************************************/
Frlist_node *frct_hash_get(u_short ip_proto
                           , u_int ui_src
                           , u_short us_port
                           , u_int ui_dest
                           , u_short us_dport)
{
    Frlist_node *p_st_node=NULL;
    char arr_hash_key[20];
    int i_hash_key_len=0;

    if(g_p_st_hash_ct==NULL)
        return NULL;

    i_hash_key_len = frct_hash_key(ip_proto, ui_src, us_port, ui_dest, us_dport, arr_hash_key, sizeof(arr_hash_key));
    p_st_node = frhash2_getStr(g_p_st_hash_ct, arr_hash_key, i_hash_key_len);

    return p_st_node;
}

/*******************************************************************************
*  FUNC     :  删除节点,并释放节点的资源
*  ARGS     :  p_st_node    (Frlist_node *)
*  RTN      :  FR_SUCC:成功, FR_FAIL:失败
*  NOTE     :
*******************************************************************************/
int frct_hash_del(Frlist_node *p_st_node)
{
    int i_ret = FR_FAIL;

    if(g_p_st_hash_ct==NULL || p_st_node == NULL)
        return i_ret;

    i_ret = frhash2_del(g_p_st_hash_ct, p_st_node, __hash_node_free);
    return i_ret;
}

/*******************************************************************************
*  FUNC     :  连接内容出力(调试用)
*  ARGS     :
*  RTN      :
*  NOTE     :  有加锁，请注意使用
*******************************************************************************/
void frct_hash_output(char **pp_result)
{
    char *p_outbuff=NULL;
    int i_outbuff_size =1024000;

    if(g_p_st_hash_ct==NULL)
        return;

    p_outbuff = (char *) frmalloc(i_outbuff_size);
    if(p_outbuff==NULL)
    {
        FRLOG_ERROR_DETAIL("frmalloc err");
        return;
    }
    ul_chk_ct_cnts=0L;
    *p_outbuff = '\0';

    frct_hash_rlock();
    frhash2_show(g_p_st_hash_ct, p_outbuff, i_outbuff_size, __hash_node_show);
    frct_hash_unrlock();

    if(pp_result == NULL)
    {
        frdbg("**************************\n");
        printf("output ct data(count=%ld)\n%s\n", ul_chk_ct_cnts, p_outbuff);
        frdbg("**************************\n");
        frfree(p_outbuff);
    }
    else
        *pp_result = p_outbuff;

    return;
}

/*******************************************************************************
*  FUNC     :  对hash表内容里运行所有函数指针
*  ARGS     :  p_data       (函数指针用数据，如果没有设置为NULL)
*           :  pf_run       (运行用函数指针)
*  RTN      :  0:pf_run控制退出; 1:运行到最后
*  NOTE     :
*******************************************************************************/
void frct_hash_run(void *p_data, list_node_run pf_run)
{
    frhash2_run(g_p_st_hash_ct, p_data, pf_run);

    return;
}

/*******************************************************************************
*  FUNC     :  连接资源释放
*  ARGS     :
*  RTN      :
*  NOTE     :
*******************************************************************************/
void frct_hash_destory(list_node_free pf_free)
{
    if(g_p_st_hash_ct == NULL)
        return;

    frhash2_free2(g_p_st_hash_ct, pf_free);
    g_p_st_hash_ct = NULL;
    pthread_spin_destroy(&g_spin_ct_lock);
    return;
}

